package org.hepeng.fabric.contract.execution.serializer;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.Validator;
import jakarta.validation.Validation;
import org.hyperledger.fabric.contract.annotation.Serializer;
import org.hyperledger.fabric.contract.execution.JSONTransactionSerializer;
import org.hyperledger.fabric.contract.metadata.TypeSchema;
import org.hyperledger.fabric.shim.ChaincodeException;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;


/**
 * @author he peng
 * @date 2022/3/23
 */

@Serializer(target = Serializer.TARGET.TRANSACTION)
public class ValidationJSONTransactionSerializer extends JSONTransactionSerializer {

    private static final Validator VALIDATOR = Validation.buildDefaultValidatorFactory().getValidator();

    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();


    @Override
    public Object fromBuffer(byte[] buffer, TypeSchema ts) {

        Object obj = super.fromBuffer(buffer, ts);

        String type = ts.getType();
        if (type.contentEquals("object") || type.contentEquals("array")) {
            validate(obj);
        }

        return obj;
    }

    private void validate(Object obj) {
        Set<ConstraintViolation<Object>> constraintViolations = VALIDATOR.validate(obj);

        if (Objects.nonNull(constraintViolations) && ! constraintViolations.isEmpty()) {
            List<Map<String , String>> errs = new ArrayList<>(constraintViolations.size());
            for (ConstraintViolation<Object> cv : constraintViolations) {
                Map<String , String> err = new HashMap<>();
                err.put("filed" , cv.getPropertyPath().toString());
                err.put("msg" , cv.getMessage());
                errs.add(err);
            }

            String errMsg;
            try {
                errMsg = String.format("Parameter validate failed: %s" , OBJECT_MAPPER.writeValueAsString(errs));
            } catch (JsonProcessingException e) {
                errMsg = e.getMessage();
            }

            throw new ChaincodeException(errMsg);
        }
    }
}
